﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Content;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler;

using Input;
using MapLibrary.Utility;

namespace GUILibrary 
{
    public class GUIScreenManager : MouseInputListener, KeyboardInputListener, ActionListener
    {
        /// <summary>
        /// Screen name to xml file name
        /// </summary>
        private Dictionary<string, string> screenNames;

        /// <summary>
        /// Screen name to GUIScreen object
        /// </summary>
        private Dictionary<string, GUIScreen> guiScreens;

        private List<ActionListener> actionListeners;
        private string currentGUIScreen;
        private List<string> currentActions;

        private string startingScreen;

        private SpriteBatch spriteBatch;

        /// <summary>
        /// GUIScreens can trigger "popup" evens.  Instead of switching to the
        /// specified GUI, it sets the popup screen.  The GUIScreenManager will
        /// then draw the popup screen on top of the current screen, and funnel
        /// all user input to it.  Then, when that screen closes, control is
        /// returned to the non-popup gui.
        /// </summary>
        private string popupScreen;

        public GUIScreenManager()
        {
            screenNames = new Dictionary<string, string>();
            guiScreens = new Dictionary<string, GUIScreen>();
            actionListeners = new List<ActionListener>();
            currentActions = new List<string>();
            popupScreen = null;
        }

        public void Initialize(GraphicsDevice graphics)
        {
            spriteBatch = new SpriteBatch(graphics);
            currentGUIScreen = StartingScreen;
        }

        public string StartingScreen
        {
            get { return startingScreen; }
            set { startingScreen = value; }
        }

        [ContentSerializerIgnore]
        public Dictionary<string, GUIScreen> GUIScreens
        {
            get { return guiScreens; }
        }

        public Dictionary<string, string> ScreenNames
        {
            get { return screenNames; }
            set { screenNames = value; }
        }

        public void Load(ContentManager cm)
        {
            foreach (string state in screenNames.Keys)
            {
                guiScreens.Add(state, cm.Load<GUIScreen>("GUI/" + screenNames[state]));
                guiScreens[state].RegisterComponents(cm);
                guiScreens[state].RegisterToManager(this);
            }

            this.AddActionListener(this);
        }

        public void UpdateMouseInput(GameInput Input)
        {
            currentActions.Clear();

            string guiToUpdate = currentGUIScreen;
            if (popupScreen != null)
                guiToUpdate = popupScreen;

            guiScreens[guiToUpdate].UpdateHover(Input.GetMousePosition());
            guiScreens[guiToUpdate].UpdateMouseClick(Input);
            if (Input.IsLeftMouseButtonPressed())
            {
                if (guiScreens[guiToUpdate].GetAction() != null)
                    currentActions.Add(guiScreens[guiToUpdate].GetAction());
            }

            foreach (string action in guiScreens[guiToUpdate].GetComponentActions())
            {
                currentActions.Add(action);
            }
        }

        public void OnMouseRegister(GameInput Input)
        {
            //do nothing
        }

        public void Draw()
        {
            spriteBatch.Begin();

            guiScreens[currentGUIScreen].Draw(spriteBatch);

            if (popupScreen != null)
                guiScreens[popupScreen].Draw(spriteBatch);

            spriteBatch.End();
        }

        /// <summary>
        /// Switch to the GUIScreen specified by the name.  If
        /// the GUIScreen doesn't exist, don't switch.
        /// </summary>
        /// <param name="name"></param>
        public void SwitchScreen(string name)
        {
            if (GUIScreens.ContainsKey(name))
                currentGUIScreen = name;
        }

        public void Update()
        {
            if(currentActions!=null)
            {
                foreach (string action in currentActions)
                {
                    if (guiScreens.ContainsKey(action))
                    {
                        currentGUIScreen = action; //NOTE: Will this cause an issue because it occurs before ActionListeners are updated?
                    }
                }
            }

            //still want to call UpdateAction, even if there aren't any actions.
            foreach (ActionListener AL in actionListeners)
            {
                AL.UpdateAction(currentActions);
            }
        }

        public void AddActionListener(ActionListener newAL)
        {
            actionListeners.Add(newAL);
        }

        public void UpdateKeyboardInput(GameInput Input)
        {
            if (GameStateManager.Instance.IsCurrentState(
                GameStateManager.GameState.MainMenu))
                return;

            if (Input.IsKeyPressed(Microsoft.Xna.Framework.Input.Keys.Escape))
            {
                if (popupScreen != null)
                    currentActions.Add("Close");
                else
                {
                    currentActions.Add("Popup:PauseMenu");
                }
            }
        }

        public void OnKeyboardRegister(GameInput Input)
        {
            //nothing
        }

        public void UpdateAction(List<string> currentActions)
        {
            if (currentActions.Contains("NewGame"))
            {
                GameStateManager.Instance.SwitchState(GameStateManager.GameState.InGame);
                SwitchScreen("InGameGUI");
            }
            else if (currentActions.Contains("Quit"))
            {
                GameStateManager.Instance.SwitchState(GameStateManager.GameState.Quitting);
            }

            foreach (string action in currentActions)
            {
                if (action.Contains("Popup:"))
                {
                    this.popupScreen = action.Remove(0, "Popup:".Length);

                    //for now assume popup means pause
                    GameStateManager.Instance.SwitchState(GameStateManager.GameState.Paused);
                }

                if (action.Contains("Close"))
                {
                    if (popupScreen != null)
                        popupScreen = null;

                    GameStateManager.Instance.SwitchState(GameStateManager.GameState.InGame);
                }
            }
        }
    }

    public class GUIScreenManagerContentReader : ContentTypeReader<GUIScreenManager>
    {
        protected override GUIScreenManager Read(ContentReader input, GUIScreenManager existingInstance)
        {
            GUIScreenManager guiScreenManager = new GUIScreenManager();
            guiScreenManager.StartingScreen = input.ReadString();
            guiScreenManager.ScreenNames = input.ReadObject<Dictionary<string, string>>();

            guiScreenManager.Load(input.ContentManager);

            return guiScreenManager;
        }
    }
}
